#ifndef FFMEDIAPLAYER_H
#define FFMEDIAPLAYER_H
#include "PacketQueue.h"
#include "FrameQueue.h"
#include "Decoder.h"
#include "AudioParams.h"
#include <atomic>
#include <memory>
#include <future>
/**
 * @brief 基于 ffplay.c 的音乐播放器
 */
class FFMediaPlayer
{
public:
    FFMediaPlayer();
    ~FFMediaPlayer();

private:

    /**
     * @brief 中断回调函数
     * @param arg: 用户数据指针
     * @return 返回值给FFmpeg内部使用 1: 退出耗时操作 0: 继续
     */
    static int decodeInterruptCb(void *arg);
    /**
     * @brief 打印错误信息
     * @param filename: 文件名
     * @param err: 错误代码
     */
    static void printError(const char *filename, int err);
    /**
     * @brief 音频数据解码，必要时重采样
     * @param is: 用户数据指针
     * @return -1没有获取到数据
     */
    static int audio_decode_frame(FFMediaPlayer *is);
    /**
     * @brief SDL音频回调函数
     * @param opaque: 用户数据
     * @param stream: SDL数据缓冲区
     * @param len: SDL缓冲区长度
     */
    static void sdlAudioCallback(void *opaque, uint8_t *stream, int len);

public:
    /**
     * @brief 开始播放
     * @param path: 音频文件名
     * @return
     */
    bool play(const char *path);
    /**
     * @brief 停止播放
     */
    void stop();
    /**
     * @brief 获取文件时长
     */
    uint32_t getDuration() { return duration; }
    /**
     * @brief 播放器是否是停止状态
     */
    bool isAbort() { return abort; }

private:
    /**
     * @brief 初始化
     * @return true: 成功 false: 失败
     */
    bool init();
    /**
     * @brief 打开对应的流成分
     * @param streamIndex: 流索引
     * @return < 0 失败
     */
    int streamComponentOpen(int streamIndex);
    /**
     * @brief 关闭对应的流成分
     * @param streamIndex: 流索引
     */
    void streamComponentClose(int streamIndex);
    /**
     * @brief 准备音频输出
     * @param opaque: 用户数据
     * @param wanted_channel_layout: 期望的音频格式
     * @param wanted_nb_channels: 期望的通道数
     * @param wanted_sample_rate: 期望的采样率
     * @param audio_hw_params: 最终目标的硬件参数
     * @return
     */
    int audioOpen(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, AudioParams &audio_hw_params);
    /**
     * @brief 判断是否有足够的包
     * @param st: 流
     * @param stream_id: 流索引
     * @param queue: 对应的流队列
     * @return true: 有足够的包
     */
    bool stream_has_enough_packets(AVStream *st, int stream_id, shared_ptr<PacketQueue> queue);
    /**
     * @brief 数据都由这里读取
     * 主要功能是做解复用，从码流中分离音视频packet，并插入缓存队列
     */
    string readThread();
    /**
     * @brief 音频解码线程
     */
    string audioThread();

private:
    string fileName;                // 文件名
    AVFormatContext *ic;            // iformat的上下文
    atomic_bool abort;              // =true时请求退出播放(中止)
    int audioIndex;                 // 音频流索引
    uint32_t volume;                // 音量
    uint32_t duration;              // 流总时长（秒）
    bool paused;                    // =true暂停，=false则播放
    bool muted;                     // =true静音，=false则正常
    bool eof;                       // =true读取结束，=false未结束
    SDL_cond *continue_read_thread; // 当读取数据队列满了后进入休眠时，可以通过该condition唤醒读线程
    SDL_AudioDeviceID audioDev;     // 音频设备句柄
    AudioParams audio_src;          // 原目标输出音频的参数
    AudioParams audio_tgt;          // 目标输出音频的参数
    Decoder auddec;                 // 音频解码器

    uint32_t audio_hw_buf_size;     // 硬件设备需要缓冲区大小
    uint32_t audio_buf_size;        // 音频数据缓冲区有效数据大小
    uint32_t audio_buf_index;       // 音频数据缓冲区读索引
    uint8_t *audio_buf = nullptr;
    uint8_t *audio_buf1 = nullptr;
    uint32_t audio_buf1_size;

    SwrContext *swr_ctx;            // 重采样器
    shared_ptr<PacketQueue> audioq; // 音频packet队列
    FrameQueue sampq;               // 采样Frame队列

    future<string> rFuture;         // 读线程返回值句柄（主要工作是为了等待读线程执行完毕）
    future<string> aFuture;         // 读线程返回值句柄（主要工作是为了等待音频解码线程执行完毕）
};

#endif // FFMUSICPLAYER_H
